#include <UnitTest++/UnitTest++.h>
#include "../Mocker.h"
#include <ostream>

using namespace MockItNow;

namespace
{
	void VoidFunction()
	{
		MOCKITNOW_ASSERT(false);
	}

	TEST (ShouldStubFunctionIsFalseForStartupState)
	{
		Mocker mocker(false);
		CHECK_EQUAL(false, mocker.ShouldStubFunction((size_t)&VoidFunction, 0));
	}

	TEST (ShouldStubFunctionIsFalseForRecordStateWhenNoFunctionsAreRegistered)
	{
		Mocker mocker(false);
		mocker.Record();
		CHECK_EQUAL(false, mocker.ShouldStubFunction((size_t)&VoidFunction, 0));
	}

	TEST (ShouldStubFunctionIsTrueForRecordStateWhenFunctionIsRegistered)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&VoidFunction);
		mocker.Record();
		CHECK_EQUAL(true, mocker.ShouldStubFunction((size_t)&VoidFunction, 0));
	}

	TEST (ShouldStubFunctionIsFalseForReplayStateWithNoRecordedFunctions)
	{
		Mocker mocker(false);
		mocker.Record();
		mocker.Replay();
		CHECK_EQUAL(false, mocker.ShouldStubFunction((size_t)&VoidFunction, 0));
	}

	TEST (ShouldStubFunctionIsTrueForReplayStateWithSameRecordedFunction)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&VoidFunction);
		mocker.Record();
		mocker.SetCurrentFunctionAddress((size_t)&VoidFunction);
		mocker.RegisterRecordFunctionCall(0, 0);
		mocker.Replay();
		CHECK_EQUAL(true, mocker.ShouldStubFunction((size_t)&VoidFunction, 0));
	}

	TEST (ShouldStubFunctionIsFalseForReplayStateWithSameRecordedFunctionOnDifferentObject)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&VoidFunction);
		mocker.Record();
		mocker.SetCurrentFunctionAddress((size_t)&VoidFunction);
		mocker.RegisterRecordFunctionCall(0, (int*)1);
		mocker.Replay();
		CHECK_EQUAL(false, mocker.ShouldStubFunction((size_t)&VoidFunction, 2));
	}

	int IntReturn()
	{
		MOCKITNOW_ASSERT(false);
		return -1;
	}

	TEST (ShouldStubFunctionIsTrueForFunctionWithDefaultReturn)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntReturn);
		mocker.DefaultStub(0);
		mocker.Record();
		mocker.Replay();
		CHECK_EQUAL(true, mocker.ShouldStubFunction((size_t)&IntReturn, 0));
	}

	void AnotherVoidFunction()
	{
	}

	TEST (ShouldStubFunctionIsTrueForReplayStateWhenFailWhenCallFunctionsAreNextInLine)
	{
		Mocker mocker(false);
		
		mocker.RegisterFunction(&VoidFunction);

		mocker.Record();
		
		VoidFunction();
		mocker.FailWhenCalled();

		VoidFunction();
		mocker.FailWhenCalled();

		VoidFunction();

		mocker.Replay();

		CHECK_EQUAL(true, mocker.ShouldStubFunction((size_t)&VoidFunction, 0));
	}

	TEST (ShouldStubFunctionIsFalseForVerifyState)
	{
		Mocker mocker(false);
		mocker.Record();
		mocker.Replay();
		mocker.Verify();
		CHECK_EQUAL(false, mocker.ShouldStubFunction((size_t)&VoidFunction, 0));
	}

	TEST (RegisterRecordFunctionCallReturnsNewFunctionCallRecord)
	{	
		Mocker mocker(false);
		mocker.Record();
		mocker.RegisterFunction(&VoidFunction);
		mocker.SetCurrentFunctionAddress((size_t)&VoidFunction);
		CHECK(mocker.RegisterRecordFunctionCall(0, 0) != 0);
	}

	TEST (RegisterReplayFunctionCallReturnsNextFunctionCallRecord)
	{	
		Mocker mocker(false);
		mocker.Record();
		mocker.RegisterFunction(&VoidFunction);
		mocker.SetCurrentFunctionAddress((size_t)&VoidFunction);
		FunctionCallRecord* record1 = mocker.RegisterRecordFunctionCall(0, 0);
		FunctionCallRecord* record2 = mocker.RegisterRecordFunctionCall(0, 0);
		mocker.Replay();
		CHECK_EQUAL(record1, mocker.RegisterReplayFunctionCall(0));
		CHECK_EQUAL(record2, mocker.RegisterReplayFunctionCall(0));
	}

	TEST (GetFunctionJumpAddressReturnsAddressOfRecordFunctionInRecordMode)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&VoidFunction);
		mocker.Record();
		mocker.SetCurrentFunctionAddress((size_t)&VoidFunction);
		size_t expected = (size_t) &FreeFunctionRecorder1<void, void>::RecordMockedFunction;
		CHECK_EQUAL(expected, mocker.GetFunctionJumpAddress());
	}

	TEST (GetFunctionJumpAddressReturnsAddressOfReplayFunctionInReplayMode)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&VoidFunction);
		mocker.Record();
		mocker.Replay();
		mocker.SetCurrentFunctionAddress((size_t)&VoidFunction);
		size_t expected = (size_t) &FreeFunctionReplayer1<void, void>::ReplayMockedFunction;
		CHECK_EQUAL(expected, mocker.GetFunctionJumpAddress());
	}

	TEST (VerifyFailsIfRecordedFunctionWasNeverCalled)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&VoidFunction);

		mocker.Record();
		VoidFunction();

		mocker.Replay();

		CHECK_THROW(mocker.Verify(), std::exception);
	}

	TEST (VerifySucceedsIfRecordedFunctionWasCalled)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&VoidFunction);

		mocker.Record();
		VoidFunction();

		mocker.Replay();
		VoidFunction();

		CHECK(mocker.Verify());
	}

	void IntArgument(const int)
	{
		MOCKITNOW_ASSERT(false);
	}

	TEST (VerifySucceedsIfFunctionPointerArgumentsMatch)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntArgument);

		void (*fct)(int) = IntArgument;

		mocker.Record();
		fct(1);

		mocker.Replay();
		fct(1);

		CHECK(mocker.Verify());
	}

	TEST (VerifySucceedsIfFunctionArgumentsMatch)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntArgument);

		mocker.Record();
		IntArgument(1);

		mocker.Replay();
		IntArgument(1);

		CHECK(mocker.Verify());
	}

	TEST (VerifyFailsIfFunctionArgumentsDoNotMatch)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntArgument);

		mocker.Record();
		IntArgument(1);

		mocker.Replay();
		IntArgument(2);

		CHECK_THROW(mocker.Verify(), std::exception);
	}

	TEST (CanIgnoreAllArguments)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntArgument);

		mocker.Record();
		IntArgument(1);
		mocker.Ignore(AllArguments);

		mocker.Replay();
		IntArgument(2);

		CHECK(mocker.Verify());
	}

	void FloatArgument(const float)
	{
		MOCKITNOW_ASSERT(false);
	}

	TEST (VerifySucceedsIfFloatFunctionArgumentsMatch)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&FloatArgument);

		mocker.Record();
		FloatArgument(1.0f);

		mocker.Replay();
		FloatArgument(1.0f);

		CHECK(mocker.Verify());
	}

	TEST (VerifyFailsIfFloatFunctionArgumentsDoNotMatch)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&FloatArgument);

		mocker.Record();
		FloatArgument(1.0f);

		mocker.Replay();
		FloatArgument(2.0f);

		CHECK_THROW(mocker.Verify(), std::exception);
	}


	void MixedArguments(int a, float b)
	{
		MOCKITNOW_ASSERT(false);
	}

	TEST (VerifySucceedsIfMixedFunctionArgumentsMatch)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&MixedArguments);

		mocker.Record();
		MixedArguments(3, 1.5f);

		mocker.Replay();
		MixedArguments(3, 1.5f);

		CHECK(mocker.Verify());
	}

	TEST (VerifySucceedsIfMixedFunctionArgumentsDoNotMatch)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&MixedArguments);

		mocker.Record();
		MixedArguments(3, 1.5f);

		mocker.Replay();
		MixedArguments(3, 2.5f);

		CHECK_THROW(mocker.Verify(), std::exception);
	}

	void ManyArguments(int a, int b, int c, int d, int e, int f)
	{
		MOCKITNOW_ASSERT(false);
	}

	TEST (VerifySucceedsIfManyFunctionArgumentsMatch)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&ManyArguments);

		mocker.Record();
		ManyArguments(1, 2, 3, 4, 5, 6);

		mocker.Replay();
		ManyArguments(1, 2, 3, 4, 5, 6);

		CHECK(mocker.Verify());
	}

	TEST (VerifySucceedsIfManyFunctionArgumentsDoNotMatch)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&ManyArguments);

		mocker.Record();
		ManyArguments(1, 2, 3, 4, 5, 6);

		mocker.Replay();
		ManyArguments(1, 2, 3, 4, 2, 6);

		CHECK_THROW(mocker.Verify(), std::exception);
	}

	void IntArguments(int, int)
	{
		// dmb vm - leave this asset even though the function is mocked so exceptions are caught.
		MOCKITNOW_ASSERT(false);
	}

	TEST (CanIgnoreSpecificArguments)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntArguments);

		mocker.Record();
		IntArguments(1, 2);
		mocker.Ignore(Argument2);

		mocker.Replay();
		IntArguments(1, 4);

		mocker.Verify();

		CHECK(true);
	}

	TEST (CanFailWhenCallingSpecificFunctions)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntArguments);

		mocker.Record();
		IntArguments(1, 2);
		mocker.FailWhenCalled();

		mocker.Replay();
		CHECK_THROW(IntArguments(1, 2), std::exception);
	}

	TEST (FailWhenCalledfunctionAtEndOfRecordedFunctionsDoesNotTriggerNotAllFunctionsReplayedError)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntArguments);

		mocker.Record();
		IntArguments(1, 2);
		mocker.FailWhenCalled();

		mocker.Replay();
		CHECK(mocker.Verify());
	}

	TEST (OtherMockedFunctionsSucceedWhenFailWhenCallFunctionIsPresent)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntArgument);
		mocker.RegisterFunction(&IntArguments);

		mocker.Record();

		IntArguments(1, 2);
		mocker.FailWhenCalled();

		IntArgument(1);
		IntArgument(2);

		mocker.Replay();

		IntArgument(1);
		IntArgument(2);

		mocker.Verify();

		CHECK(true);
	}

	struct Dummy
	{ 
		void Test()
		{
			++value;
		}

		int value;
	};

	TEST(ReplayChecksContext)
	{
		Mocker mocker(false);

		Dummy dummy1;
		Dummy dummy2;

		mocker.RegisterFunction(&Dummy::Test);
		mocker.DefaultStub();

		mocker.Record();		
		dummy1.Test();
		
		mocker.Replay();
		dummy2.Test();

		CHECK_THROW(mocker.Verify(), FailureException);
	}

	TEST(ReplayAllowsCallsToMemberFunctionsOnObjectsThatWereNeverRecorded)
	{
		Mocker mocker(false);

		Dummy dummy1;
		Dummy dummy2;
		dummy2.value = 1;

		mocker.RegisterFunction(&Dummy::Test);

		mocker.Record();
		
		dummy1.Test();
		
		mocker.Replay();

		dummy1.Test();
		dummy2.Test();

		CHECK_EQUAL(2, dummy2.value);
	}

	TEST(CanIgnoreThis)
	{
		Mocker mocker(false);

		Dummy dummy1;
		Dummy dummy2;

		mocker.RegisterFunction(&Dummy::Test);
		mocker.Record();
		
		dummy1.Test();
		mocker.Ignore(This);

		mocker.Replay();
		dummy2.Test();

		mocker.Verify();
		CHECK(true);
	}

	void IntPointerArgument(const int*)
	{
		MOCKITNOW_ASSERT(false);
	}

	TEST (CanAlterTheCompareFunction)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntPointerArgument);

		int a = 5;

		mocker.Record();
		IntPointerArgument(&a);
		mocker.SetCompareFunction<int*>(Argument1, &CompareByValue);
		a = 5;

		mocker.Replay();
		IntPointerArgument(&a);

		CHECK(mocker.Verify());
	}

	void VoidPointerIntPointerArguments(const void*, int*)
	{
		MOCKITNOW_ASSERT(false);
	}

	TEST (CanSetCompareFunctionPerArgument)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&VoidPointerIntPointerArguments);

		int a = 5;
		int b = 6;

		mocker.Record();
		VoidPointerIntPointerArguments(&a, &b);
		mocker.SetCompareFunction<const void*>(Argument1, &CompareByValue);
		mocker.SetCompareFunction<int*>(Argument2, &CompareByValue);

		a = 10;

		mocker.Replay();
		VoidPointerIntPointerArguments(&a, &b);

		mocker.Verify();

		CHECK(true);
	}

	TEST (CanMockIntReturnValues)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntReturn);

		mocker.Record();
		IntReturn();
		mocker.Return(0);

		mocker.Replay();
		const int result = IntReturn();

		CHECK_EQUAL(0, result);
		CHECK(mocker.Verify());
	}

	TEST (ReturnAlwaysAlwaysReturnsTheSameValue)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntReturn);

		mocker.Record();
		IntReturn();
		mocker.ReturnAlways(0);

		mocker.Replay();
		IntReturn();
		IntReturn();
		IntReturn();
		const int result = IntReturn();

		CHECK_EQUAL(0, result);
		CHECK(mocker.Verify());
	}

	TEST (ReplayFailsIfReturnValueIsOmitted)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntReturn);

		mocker.Record();
		IntReturn();

		mocker.Replay();
		CHECK_THROW(IntReturn(), std::exception);
	}

	int kGlobalInt1 = 1;
	int kGlobalInt2 = 2;

	const int& IntReferenceReturn()
	{
		MOCKITNOW_ASSERT(false);
		return kGlobalInt1;
	}

	TEST (CanMockReferenceReturnValues)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntReferenceReturn);

		mocker.Record();
		IntReferenceReturn();
		mocker.Return(0);

		mocker.Replay();
		const int result = IntReferenceReturn();

		CHECK_EQUAL(0, result);
		CHECK(mocker.Verify());
	}

	const int& ConstIntReferenceReturn()
	{
		MOCKITNOW_ASSERT(false);
		return kGlobalInt1;
	}

	TEST (CanMockConstReferenceReturnValues)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&ConstIntReferenceReturn);

		mocker.Record();
		ConstIntReferenceReturn();
		mocker.Return(0);

		mocker.Replay();
		const int result = ConstIntReferenceReturn();

		CHECK_EQUAL(0, result);
		CHECK(mocker.Verify());
	}

	int* IntPointerReturn()
	{
		MOCKITNOW_ASSERT(false);
		return &kGlobalInt1;
	}

	TEST (CanMockPointerReturnValues)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntPointerReturn);

		mocker.Record();
		IntPointerReturn();
		mocker.Return(&kGlobalInt2);

		mocker.Replay();
		const int* result = IntPointerReturn();

		CHECK_EQUAL(&kGlobalInt2, result);
		CHECK(mocker.Verify());
	}

	const int kGlobalConstInt1 = 1;
	const int kGlobalConstInt2 = 2;

	const int* ConstIntPointerReturn()
	{
		MOCKITNOW_ASSERT(false);
		return &kGlobalConstInt1;
	}

	TEST (CanMockConstPointerReturnValues)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&ConstIntPointerReturn);

		mocker.Record();
		ConstIntPointerReturn();
		mocker.Return(&kGlobalConstInt2);

		mocker.Replay();
		const int* result = ConstIntPointerReturn();

		CHECK_EQUAL(&kGlobalConstInt2, result);
		CHECK(mocker.Verify());
	}

	struct Custom
	{
		Custom()
		: value(0)
		{
		}

		Custom(int value_)
		: value(value_)
		{
		}

		virtual ~Custom()
		{
		}

		void IntArgument(int argument)
		{
			value = argument;
		}

		void AssertingFunction(int)
		{
			MOCKITNOW_ASSERT(false);
		}

		virtual void VirtualIntArgument(int argument)
		{
			value = argument;
		}

		virtual const Custom* CustomPointerReturn() const
		{
			return this;
		}

		int value;
	};

	Custom CustomReturn()
	{
		//MOCKITNOW_ASSERT(false);
		return Custom(0);
	}

	TEST (CanMockCustomReturnValues)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&CustomReturn);

		mocker.Record();
		CustomReturn();
		mocker.Return(Custom(1));

		mocker.Replay();
		const Custom result = CustomReturn();

		CHECK_EQUAL(1, result.value);
		CHECK(mocker.Verify());
	}

	TEST (CanMockMemberFunctions)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&Custom::IntArgument);

		Custom custom(1);

		mocker.Record();
		custom.IntArgument(10);

		mocker.Replay();
		custom.IntArgument(10);

		CHECK_EQUAL(1, custom.value);
		CHECK(mocker.Verify());		
	}

	TEST (CanMockVirtualMemberFunctions)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&Custom::VirtualIntArgument);
	
		Custom custom(1);


		mocker.Record();
		custom.VirtualIntArgument(10);

		mocker.Replay();
		custom.VirtualIntArgument(10);

		CHECK_EQUAL(1, custom.value);
		CHECK(mocker.Verify());		
	}

	TEST (CanMockVirtualFunctionReturnValues)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&Custom::CustomPointerReturn);

		Custom custom(1);
		Custom customReturn(2);

		mocker.Record();
		custom.CustomPointerReturn();
		mocker.Return(&customReturn);

		mocker.Replay();
		const Custom* result = custom.CustomPointerReturn();

		CHECK_EQUAL(&customReturn, result);
		CHECK(mocker.Verify());		
	}

	TEST (MatchesReplayedFunctionsBasedOnTarget)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&Custom::IntArgument);
		mocker.DefaultStub();

		Custom custom1;
		Custom custom2;

		mocker.Record();
		custom2.IntArgument(0);

		mocker.Replay();
		custom1.IntArgument(1);
		custom2.IntArgument(0);

		CHECK(mocker.Verify());
	}

	TEST(ReplayThrowsFailureExceptionWhenReplayingAFunctionThatIsRegisteredButDoesNotHaveADefaultStub)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&Custom::AssertingFunction);

		Custom custom1;
		Custom custom2;

		mocker.Record();
		custom2.AssertingFunction(0);

		mocker.Replay();
		CHECK_THROW(custom1.AssertingFunction(0), FailureException);
	}

	TEST (CanSetDefaultReturnValues)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntReturn);
		mocker.DefaultStub(143);

		mocker.Record();
		mocker.Replay();

		const int result = IntReturn();

		CHECK_EQUAL(143, result);
		CHECK(mocker.Verify());
	}

	TEST (CannotSetDefaultReturnOnFunctionWithNoReturnValue)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&VoidFunction);
		CHECK_THROW(mocker.DefaultStub(143), std::exception);
	}

	TEST (RecordedFunctionOverridesDefaultReturn)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&IntReturn);
		mocker.DefaultStub(143);

		mocker.Record();
		IntReturn();
		mocker.Return(245);

		mocker.Replay();
		const int result = IntReturn();

		CHECK_EQUAL(245, result);
		CHECK(mocker.Verify());
	}

	void OutIntReference(int&)
	{
		MOCKITNOW_ASSERT(false);
	}

	TEST (CanMockOutReferenceArguments)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&OutIntReference);

		mocker.Record();
		int value = 0;
		OutIntReference(value);
		mocker.Return(Argument1, 245);

		mocker.Replay();
		int result = 0;
		OutIntReference(result);

		CHECK_EQUAL(245, result);
		CHECK(mocker.Verify());
	}

	void OutIntPointer(int*)
	{
		MOCKITNOW_ASSERT(false);
	}

	TEST (CanMockOutArguments)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&OutIntPointer);

		mocker.Record();
		int value = 0;
		int result = 245;
		OutIntPointer(&value);
		mocker.Return(Argument1, &result);

		value = 0;
		mocker.Replay();
		OutIntPointer(&value);

		CHECK_EQUAL(245, value);
		CHECK(mocker.Verify());
	}

	struct LargeData
	{
		char data[1000];

		LargeData()
		{
		}

		bool operator==(const LargeData&) const
		{
			return true;
		}
	};

	std::ostream& operator<<(std::ostream& stream, const LargeData&)
	{
		return stream;
	}


	void LargeArgument(LargeData)
	{
	}

	TEST (CanMockOutFreeFunctionsTakingLargeArguments)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&LargeArgument);
		LargeData data;

		mocker.Record();
		LargeArgument(data);

		mocker.Replay();
		LargeArgument(data);

		CHECK(mocker.Verify());
	}

	LargeData LargeReturnValue()
	{
		return LargeData();
	}

	TEST (CanMockOutFreeFunctionsReturningLargeData)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&LargeReturnValue);
		LargeData data;

		mocker.Record();
		LargeReturnValue();
		mocker.Return(data);

		mocker.Replay();
		data = LargeReturnValue();

		CHECK(mocker.Verify());
	}

	struct LargeArgumentClass
	{
		void LargeArgument(LargeData)
		{
		}
	};

	TEST (CanMockOutMemberFunctionsTakingLargeArguments)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&LargeArgumentClass::LargeArgument);
		LargeData data;
		LargeArgumentClass object;

		mocker.Record();
		object.LargeArgument(data);

		mocker.Replay();
		object.LargeArgument(data);

		CHECK(mocker.Verify());
	}

	struct LargeReturnValueClass
	{
		LargeData LargeReturnValue()
		{
			return LargeData();
		}
	};

	TEST (CanMockOutMemberFunctionsReturningLargeData)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&LargeReturnValueClass::LargeReturnValue);
		LargeData data;
		LargeReturnValueClass object;

		mocker.Record();
		object.LargeReturnValue();
		mocker.Return(data);

		mocker.Replay();
		data = object.LargeReturnValue();

		CHECK(mocker.Verify());
	}

	void StringFunction(const char*)
	{
	}

	TEST(PassesIfStringParametersAreEqual)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&StringFunction);

		mocker.Record();
		StringFunction("foo");

		mocker.Replay();

		char str[4];
		strcpy(str, "foo");
		StringFunction(str);

		mocker.Verify();

		CHECK(true);
	}

	TEST(FailsIfStringParametersAreUnequal)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&StringFunction);

		mocker.Record();
		StringFunction("foo");

		mocker.Replay();

		StringFunction("bar");

		CHECK_THROW(mocker.Verify(), FailureException);
	}

	void Testfunction(int& i)
	{
		i = 987;
	}

	TEST(CallsOriginal)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&Testfunction);

		int i = 0;

		mocker.Record();
		Testfunction(i);
		mocker.CallOriginal();
		
		mocker.Replay();
		Testfunction(i);

		CHECK_EQUAL(987, i);

		CHECK_EQUAL(true, mocker.Verify());	
	}

	struct ReturnAlwaysClass
	{
		int ReturnAlwaysFunction()
		{
			return 0;
		}

		int ReturnOnceFunction()
		{
			return 0;
		}
	};

	TEST(ReturnAlwaysExpectationsDontNeedToBeRecordedInTheReplayOrder)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&ReturnAlwaysClass::ReturnAlwaysFunction);
		mocker.RegisterFunction(&ReturnAlwaysClass::ReturnOnceFunction);

		ReturnAlwaysClass object1;
		ReturnAlwaysClass object2;

		mocker.Record();

		object1.ReturnAlwaysFunction();
		mocker.ReturnAlways(1);

		object2.ReturnAlwaysFunction();
		mocker.ReturnAlways(2);

		object1.ReturnOnceFunction();
		mocker.Return(3);

		object2.ReturnOnceFunction();
		mocker.Return(4);

		mocker.Replay();

		CHECK_EQUAL(1, object1.ReturnAlwaysFunction());
		CHECK_EQUAL(3, object1.ReturnOnceFunction());
		CHECK_EQUAL(2, object2.ReturnAlwaysFunction());
		CHECK_EQUAL(4, object2.ReturnOnceFunction());

		CHECK_EQUAL(true, mocker.Verify());	
	}

	void FunctionThatThrowsException()
	{
		throw 1;
	}

	void FunctionCallsFunctionThatThrowsException()
	{
		FunctionThatThrowsException();
	}

	TEST (CanThrowExceptions)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&FunctionThatThrowsException);

		mocker.Record();

		FunctionThatThrowsException();
		mocker.ThrowException(123456);

		mocker.Replay();

		bool caught = false;

		try
		{
			FunctionCallsFunctionThatThrowsException();
		}
		catch (const int& e)
		{
			CHECK(e == 123456);
			caught = true;
		}

		CHECK(caught);
		CHECK_EQUAL(true, mocker.Verify());
	}

	TEST (CanThrowExceptionsByDefault)
	{
		Mocker mocker(false);
		mocker.RegisterFunction(&FunctionThatThrowsException);
		mocker.DefaultThrowException(123456);

		mocker.Record();
		mocker.Replay();

		bool caught = false;

		try
		{
			FunctionCallsFunctionThatThrowsException();
		}
		catch (const int& e)
		{
			CHECK(e == 123456);
			caught = true;
		}

		CHECK(caught);
		CHECK_EQUAL(true, mocker.Verify());
	}
}
